package mylittleinvestigations;

import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.GameUi.Tab;
import mylittleinvestigations.GameUi.Arrow;
import mylittleinvestigations.GameUi.ArrowDirection;
import java.util.ArrayList;
import mylittleinvestigations.EasingFunctions.EasingFunction;
import mylittleinvestigations.EasingFunctions.GravityBounceEase;
import mylittleinvestigations.EasingFunctions.QuadraticEase;
import mylittleinvestigations.Events.EvidenceSelectorEventProvider;
import mylittleinvestigations.PersistentThreads.RenderThread;
import org.dawnmew.jpurewave.SoundSystem;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.Rectangle;

public class EvidenceSelector
{
    private static Image containerTexture;
    private static Image selectionTexture;
    private static Image descriptionTexture;
    private static UnicodeFont descriptionFont;

    private static int itemHorizontalSpacing = 71;
    private static int itemVerticalSpacing = 71;
    private static int numItemsPerRow = 10;
    private static int numRows = 2;

    private static int descriptionXOffset = 59;
    private static int descriptionYOffset = 151;

    private int animationOffset;

    private EvidenceSelectorItem[] items;

    private EvidenceSelectorItem selectedItem;
    private EvidenceSelectorItem itemSetAsideForCombination;
    private Sprite selectedEvidenceSprite;
    private String selectedEvidenceDescription;

    private Tab presentTab;
    private Tab combineWithTab;
    private Tab combineTab;
    private Tab cancelTab;

    private Arrow leftArrow;
    private Arrow rightArrow;

    private boolean isCancelable;
    private boolean isForCombination;

    private EasingFunction inEase;
    private EasingFunction outEase;

    public static void Initialize(Image containerTexture, Image containerSelectionTexture, Image descriptionTexture, UnicodeFont descriptionFont) throws SlickException
    {
        EvidenceSelector.containerTexture = containerTexture;
        EvidenceSelector.selectionTexture = containerSelectionTexture;
        EvidenceSelector.descriptionTexture = descriptionTexture;
        EvidenceSelector.descriptionFont = descriptionFont;
    }

    public EvidenceSelector(boolean isCancelable)
    {
        this(isCancelable, false);
    }

    public EvidenceSelector(boolean isCancelable, boolean isForCombination)
    {
        this.presentTab = new Tab((int)Settings.DisplayBounds.getX() - Tab.Width - (isCancelable ? Tab.Width + 7 : 0), true /* isClickable */, "PRESENT");
        this.combineWithTab = new Tab((int)Settings.DisplayBounds.getX() - Tab.Width - (isCancelable ? Tab.Width + 7 : 0), true /* isClickable */, "COMBINE WITH");
        this.combineTab = new Tab((int)Settings.DisplayBounds.getX() - Tab.Width - (isCancelable ? Tab.Width + 7 : 0), true /* isClickable */, "COMBINE");
        this.cancelTab = new Tab((int)Settings.DisplayBounds.getX() - Tab.Width, true /* isClickable */, "CANCEL", true /* isCancel */);

        this.presentTab.setIsEnabled(false);
        this.combineWithTab.setIsEnabled(false);
        this.combineTab.setIsEnabled(false);
        this.cancelTab.setIsEnabled(true);

        this.leftArrow = new Arrow(82, 63, ArrowDirection.Left, 10 /* bounceDistance */, true /* isClickable */);
        this.rightArrow = new Arrow(858, 63, ArrowDirection.Right, 10 /* bounceDistance */, true /* isClickable */);

        this.leftArrow.setIsEnabled(false);
        this.rightArrow.setIsEnabled(false);

        this.items = new EvidenceSelectorItem[20];

        for (int y = 0; y < numRows; y++)
        {
            for (int x = 0; x < numItemsPerRow; x++)
            {
                this.items[x + y * numItemsPerRow] = new EvidenceSelectorItem(128 + x * itemHorizontalSpacing + (x > numItemsPerRow / 2 ? 1 : 0), 11 + y * itemVerticalSpacing);
            }
        }

        this.selectedItem = null;
        this.selectedEvidenceSprite = null;
        this.selectedEvidenceDescription = null;

        this.isCancelable = isCancelable;
        this.isForCombination = isForCombination;

        this.inEase = new QuadraticEase(Settings.DisplayBounds.getX(), 0, 250);
        this.outEase = new QuadraticEase(0, -Settings.DisplayBounds.getX(), 250);
    }
    
    private boolean isShowing;
    
    public boolean getIsShowing() { return this.isShowing; }
    public void setIsShowing(boolean isShowing) { this.isShowing = isShowing; }

    private boolean getAcceptsInput()
    {
        return this.inEase.getIsFinished() && !this.outEase.getIsStarted();
    }
    
    private Conversation evidenceCombinationConversation;
    
    public Conversation getEvidenceCombinationConversation() { return this.evidenceCombinationConversation; }
    public void setEvidenceCombinationConversation(Conversation evidenceCombinationConversation) { this.evidenceCombinationConversation = evidenceCombinationConversation; }

    public void Begin()
    {
        this.Reset();

        ArrayList<String> evidenceIds = Case.getInstance().getEvidenceManager().GetIds();
        int lastItemIndex = 0;

        for (int i = 0; i < numRows * numItemsPerRow && i < evidenceIds.size(); i++)
        {
            if (Case.getInstance().getEvidenceManager().IsEvidenceWithIdVisible(evidenceIds.get(i)))
            {
                this.items[lastItemIndex++].LoadEvidence(evidenceIds.get(i));
            }
        }

        if (this.items.length > 0)
        {
            this.items[0].setIsSelected(true);
        }

        this.setEvidenceCombinationConversation(null);
        this.combineWithTab.setIsEnabled(Case.getInstance().getEvidenceManager().getCanCombineEvidence());
    }

    public void Update(GameContainer gc, int delta)
    {
        if (this.outEase.getIsStarted() && !this.outEase.getIsFinished())
        {
            this.outEase.Update(gc, delta);
            this.animationOffset = (int)this.outEase.GetCurrentValue();
        }
        else if (this.outEase.getIsFinished())
        {
            this.setIsShowing(false);
        }
        else if (this.inEase.getIsStarted() && !this.inEase.getIsFinished())
        {
            this.inEase.Update(gc, delta);
            this.animationOffset = (int)this.inEase.GetCurrentValue();
        }
        else
        {
            if (this.isForCombination)
            {
                if (this.itemSetAsideForCombination != null)
                {
                    this.combineTab.Update();
                }
                else
                {
                    this.combineWithTab.Update();
                }
            }
            else
            {
                this.presentTab.Update();
            }

            if (this.isCancelable)
            {
                this.cancelTab.Update();
            }

            this.leftArrow.Update(gc, delta);
            this.rightArrow.Update(gc, delta);

            for (int i = 0; i < this.items.length; i++)
            {
                this.items[i].Update(gc, delta);
                this.UpdateSelectedItem(this.items[i]);
            }

            if (this.combineTab.getIsClicked())
            {
                this.setEvidenceCombinationConversation(Case.getInstance().getEvidenceManager().GetConversationForEvidenceCombination(this.itemSetAsideForCombination.getEvidenceId(), this.selectedItem.getEvidenceId()));
                this.Close();
            }
            else if (this.combineWithTab.getIsClicked())
            {
                this.selectedItem.setIsSetAsideForCombination(true);
                this.itemSetAsideForCombination = this.selectedItem;

                for (int i = 0; i < this.items.length; i++)
                {
                    if (!this.items[i].getIsSetAsideForCombination() && this.items[i].getEvidenceId() != null)
                    {
                        this.items[i].setIsSelected(true);
                        this.UpdateSelectedItem(this.items[i]);
                        break;
                    }
                }

                this.combineWithTab.Reset();
            }
            else if (this.presentTab.getIsClicked())
            {
                this.OnEvidencePresented(this.selectedItem.getEvidenceId());
                this.Close();
            }

            if (this.cancelTab.getIsClicked())
            {
                if (this.itemSetAsideForCombination != null)
                {
                    this.itemSetAsideForCombination.setIsSetAsideForCombination(false);
                    this.itemSetAsideForCombination.setIsSelected(true);
                    this.UpdateSelectedItem(this.itemSetAsideForCombination);
                    this.itemSetAsideForCombination = null;

                    this.combineTab.Reset();
                }
                else
                {
                    this.Close();
                }
            }
        }
    }

    public void UpdateState()
    {
        if (this.getAcceptsInput())
        {
            if (this.isForCombination)
            {
                if (this.itemSetAsideForCombination != null)
                {
                    this.combineTab.Update();
                }
                else
                {
                    this.combineWithTab.Update();
                }
            }
            else
            {
                this.presentTab.Update();
            }

            if (this.isCancelable)
            {
                this.cancelTab.Update();
            }

            this.leftArrow.UpdateState();
            this.rightArrow.UpdateState();

            for (int i = 0; i < this.items.length; i++)
            {
                this.items[i].UpdateState();
                this.UpdateSelectedItem(this.items[i]);
            }

            if (this.presentTab.getIsClicked())
            {
                this.OnEvidencePresented(this.selectedItem.getEvidenceId());
                this.Close();
            }
            if (this.cancelTab.getIsClicked())
            {
                this.Close();
            }
        }
    }

    public void Draw(GameContainer gc, Graphics g) throws SlickException
    {
        this.Draw(gc, g, 0);
    }

    public void Draw(GameContainer gc, Graphics g, float yOffset) throws SlickException
    {
        RenderThread.getInstance().AddImageToRenderQueue(descriptionTexture, descriptionXOffset + this.animationOffset, descriptionYOffset + yOffset);

        this.leftArrow.Draw(gc, g, this.animationOffset, yOffset);
        this.rightArrow.Draw(gc, g, this.animationOffset, yOffset);

        for (int i = 0; i < this.items.length; i++)
        {
            this.items[i].Draw(gc, g, this.animationOffset, yOffset);
        }

        if (this.selectedEvidenceSprite != null)
        {
            this.selectedEvidenceSprite.Draw(gc, g, new Vector2(descriptionXOffset + 3 + this.animationOffset, descriptionYOffset + 3 + yOffset));
            RenderThread.getInstance().AddStringToRenderQueue(descriptionFont, descriptionXOffset + 128 + 20 + this.animationOffset, descriptionYOffset + 130 / 2 - descriptionFont.getHeight(this.selectedEvidenceDescription) / 2 + yOffset, this.selectedEvidenceDescription);
        }

        if (this.isForCombination)
        {
            if (this.itemSetAsideForCombination != null)
            {
                this.combineTab.Draw(gc, g);
            }
            else
            {
                this.combineWithTab.Draw(gc, g);
            }
        }
        else
        {
            this.presentTab.Draw(gc, g);
        }

        if (this.isCancelable)
        {
            this.cancelTab.Draw(gc, g);
        }
    }

    public void Reset()
    {
        this.presentTab.Reset();
        this.combineWithTab.Reset();
        this.combineTab.Reset();
        this.cancelTab.Reset();

        this.leftArrow.Reset();
        this.rightArrow.Reset();

        for (int i = 0; i < this.items.length; i++)
        {
            this.items[i].Reset();
        }

        this.itemSetAsideForCombination = null;
        this.selectedItem = null;
        this.selectedEvidenceSprite = null;
        this.selectedEvidenceDescription = null;

        this.setIsShowing(false);

        this.inEase.Reset();
        this.outEase.Reset();
    }

    public void Show()
    {
        this.setIsShowing(true);
        this.animationOffset = (int)Settings.DisplayBounds.getX();

        if (this.selectedItem != null)
        {
            this.selectedItem.setIsSelected(false);
        }

        if (this.items.length > 0)
        {
            this.items[0].setIsSelected(true);
        }

        this.inEase.Reset();
        this.outEase.Reset();

        this.inEase.Begin();
    }

    public void Close()
    {
        this.outEase.Begin();
    }

    private void UpdateSelectedItem(EvidenceSelectorItem item)
    {
        if (item.getIsSelected() && this.selectedItem != item)
        {
            if (this.selectedItem != null)
            {
                this.selectedItem.setIsSelected(false);
            }

            this.selectedItem = item;
            this.selectedEvidenceSprite = Case.getInstance().getEvidenceManager().GetLargeSpriteForId(item.getEvidenceId());
            this.selectedEvidenceDescription = Case.getInstance().getEvidenceManager().GetDescriptionForId(item.getEvidenceId());
        }

        this.presentTab.setIsEnabled(this.selectedItem != null);
        this.combineWithTab.setIsEnabled(Case.getInstance().getEvidenceManager().getCanCombineEvidence() && this.selectedItem != null);
        this.combineTab.setIsEnabled(this.selectedItem != null);
    }

    private void OnEvidencePresented(String evidenceId)
    {
        EvidenceSelectorEventProvider.RaiseEvidencePresented(this, evidenceId);
    }

    private class EvidenceSelectorItem
    {
        private Vector2 position;
        private Rectangle boundingRect;
        private Sprite evidenceSprite;

        private float selectionOffset;
        private EasingFunction selectionOffsetEase;

        private String clickSoundEffect="ButtonClick3";
        
        private String evidenceId;
        
        public String getEvidenceId() { return this.evidenceId; }
        private void setEvidenceId(String evidenceId) { this.evidenceId = evidenceId; }
        
        private boolean isSelected;
        
        public boolean getIsSelected() { return this.isSelected; }
        private void setIsSelected(boolean isSelected) { this.isSelected = isSelected; }
        
        private boolean isSetAsideForCombination;
        
        public boolean getIsSetAsideForCombination() { return this.isSetAsideForCombination; }
        private void setIsSetAsideForCombination(boolean isSetAsideForCombination) { this.isSetAsideForCombination = isSetAsideForCombination; }

        public String getClickSoundEffect()
        {
            return this.clickSoundEffect;
        }

        public EvidenceSelectorItem(int xPosition, int yPosition)
        {
            this.position = new Vector2(xPosition, yPosition);
            this.boundingRect = new Rectangle(xPosition, yPosition, containerTexture.getWidth(), containerTexture.getHeight());

            this.selectionOffset = 0;
            this.selectionOffsetEase = new GravityBounceEase(0, 10, 300);
        }

        public void LoadEvidence(String evidenceId)
        {
            this.setEvidenceId(evidenceId);
            this.evidenceSprite = Case.getInstance().getEvidenceManager().GetSmallSpriteForId(evidenceId);
        }

        public void Update(GameContainer gc, int delta)
        {
            this.UpdateState();

            if (this.getIsSelected())
            {
                if (!this.selectionOffsetEase.getIsStarted() || this.selectionOffsetEase.getIsFinished())
                {
                    this.selectionOffsetEase.Begin();
                }

                this.selectionOffsetEase.Update(gc, delta);
                this.selectionOffset = (float)this.selectionOffsetEase.GetCurrentValue();
            }
            else
            {
                this.selectionOffset = 0;
            }
        }

        public void UpdateState()
        {
            if (this.getEvidenceId() != null && !this.getIsSetAsideForCombination())
            {
                if (MouseHelper.MouseOverRect(this.boundingRect) || MouseHelper.MouseDownOnRect(this.boundingRect))
                {
                    MouseHelper.SetToHandCursor();
                }

                if (MouseHelper.ClickedOnRect(this.boundingRect))
                {
                    this.setIsSelected(true);
                    SoundSystem.getInstance().playSound(getClickSoundEffect());
                }
            }
        }

        public void Draw(GameContainer gc, Graphics g, float xOffset, float yOffset)
        {
            RenderThread.getInstance().AddImageToRenderQueue(containerTexture, (float)position.getX() + xOffset, (float)position.getY() + yOffset);

            if (this.evidenceSprite != null)
            {
                this.evidenceSprite.Draw(gc, g, new Vector2(position.getX() + 3 + xOffset, position.getY() + 3 + yOffset), this.getIsSetAsideForCombination() ? new Color(128, 128, 128, 255) : Color.white);
            }

            if (this.getIsSelected())
            {
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() - 10 + this.selectionOffset) + xOffset, (float)(position.getY() - 10 + this.selectionOffset) + yOffset);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() + containerTexture.getWidth() - selectionTexture.getWidth() + 10 - this.selectionOffset) + xOffset, (float)(position.getY() - 10 + this.selectionOffset) + yOffset, Color.white, true /* flipHorizontal */, false /* flipVertical */);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() - 10 + this.selectionOffset) + xOffset, (float)(position.getY() + containerTexture.getHeight() - selectionTexture.getHeight() + 10 - this.selectionOffset) + yOffset, Color.white, false /* flipHorizontal */, true /* flipVertical */);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() + containerTexture.getWidth() - selectionTexture.getWidth() + 10 - this.selectionOffset) + xOffset, (float)(position.getY() + containerTexture.getHeight() - selectionTexture.getHeight() + 10 - this.selectionOffset) + yOffset, Color.white, true /* flipHorizontal */, true /* flipVertical */);
            }
            else if (this.getIsSetAsideForCombination())
            {
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)position.getX() + xOffset, (float)position.getY() + yOffset);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() + containerTexture.getWidth() - selectionTexture.getWidth()) + xOffset, (float)position.getY() + yOffset, Color.white, true /* flipHorizontal */, false /* flipVertical */);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)position.getX() + xOffset, (float)(position.getY() + containerTexture.getHeight() - selectionTexture.getHeight()) + yOffset, Color.white, false /* flipHorizontal */, true /* flipVertical */);
                RenderThread.getInstance().AddImageToRenderQueue(selectionTexture, (float)(position.getX() + containerTexture.getWidth() - selectionTexture.getWidth()) + xOffset, (float)(position.getY() + containerTexture.getHeight() - selectionTexture.getHeight()) + yOffset, Color.white, true /* flipHorizontal */, true /* flipVertical */);
            }
        }

        public void Reset()
        {
            this.setIsSelected(false);
            this.setIsSetAsideForCombination(false);
            this.setEvidenceId(null);
            this.evidenceSprite = null;
        }
    }
}